昨天,我們終於將 Todo List 的功能完成,今天開始,我們要來探索一些先前沒有機會用到的小技巧以及觀念。
在 Todo List 中,所有的 <input>
都是獨立的,對於 React 開發者來說,當有多個地方使用到 <input>
或 <button>
... 等時,我們會習慣將它直接作為一個元件,藉由傳入不同的 props
,讓這個元件可以重複利用於不同的情境下。
今天我們以 <input>
來探索在 React 中使用 TypeScript 的小技巧。
首先,創建一個 Input
元件:
export default function Input() {
return (
<div>
<label htmlFor=''></label>
<input id='' />
</div>
)
}
label
的內容以及 id
我們會透過 props
傳入,因此在這邊需要為 props
定義型別:
type InputProps = {
label: string
id: string
}
export default function Input({ label, id }: InputProps) {
return (
<div>
<label htmlFor={id}>{label}</label>
<input id={id} />
</div>
)
}
回到 App.tsx
並匯入 Input
元件,我們可以透過傳遞不同的資訊,創建內容不同的 Input
:
import './App.css'
import Input from './components/Input'
function App() {
return (
<main>
<Input id='account' label='Account' />
<Input id='password' label='Password' />
</main>
)
}
export default App
目前我們僅僅是傳遞 id
以及 label
給 Input
元件,若我們需要傳入其他 <input>
可以使用的屬性呢?
常見的寫法是運用展開運算子:
export default function Input({ label, id, ...props }: InputProps) {
return (
<div>
<label htmlFor={id}>{label}</label>
<input id={id} {...props} />
</div>
)
}
預估達到的效果是可以對 Input
元件傳入其他屬性,像是 type
:
function App() {
return (
<main>
<Input id='account' label='Account' type='text' />
<Input id='password' label='Password' type='password' />
</main>
)
}
但這時候會收到錯誤訊息:
這是因為我們在 InputProps
中只定義了 id
與 label
的型別,並沒有為 type
定義型別,在這邊我們會使用到 Day08 所介紹的合併型別,合併我們指定的型別 (id
& label
) 以及 <input>
所有內建方法、屬性的型別。
而 React 已經幫我們內建好了這個型別,也就是 ComponentPropsWithoutRef
。
合併 ComponentPropsWithoutRef
型別,並於泛型參數內傳入 input
,告知 React 我們要使用的是 input
這個元素:
import { ComponentPropsWithoutRef } from 'react'
type InputProps = {
label: string
id: string
} & ComponentPropsWithoutRef<'input'>
export default function Input({ label, id, ...props }: InputProps) {
return (
<div>
<label htmlFor={id}>{label}</label>
<input id={id} {...props} />
</div>
)
}
ComponentPropsWithRef
與 ComponentPropsWithoutRef
的差別僅在於接收的 props
是否包含 ref
,適合用於使用 forwardRef
時,而我們目前的範例不需要傳入 ref
,所以選擇使用 ComponentPropsWithoutRef
。
打開瀏覽器,前往 http://localhost:5173/,你可以看見 password
欄位的 type
已經正確指定為 password
的形式。